home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 399_01 / mined2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-03  |  56.3 KB  |  2,339 lines

  1. /*  ==================================================================    *
  2.  *                Editor mined                *
  3.  *                Part 2                    *
  4.  *  ==================================================================    */
  5.  
  6. #include "mined.h"
  7.  
  8. /*  ==================================================================    *
  9.  *            Definitions specific for mined2.c        *
  10.  *  ==================================================================    */
  11.  
  12. #ifndef copycommand
  13. # ifdef vms
  14. # define copycommand "copy %s %s"
  15. # endif
  16. # ifdef msdos
  17. # define copycommand "copy %s %s > nul:"
  18. # endif
  19. #endif
  20.  
  21. /*
  22.  * viewonlyerr () outputs an error message with a beep
  23.  */
  24. void
  25. viewonlyerr ()
  26. {
  27.   ring_bell ();
  28.   error ("View only mode", NIL_PTR);
  29. }
  30.  
  31. FLAG yank_status = NOT_VALID;        /* Status of yank_file */
  32.  
  33. /*  ==================================================================    *
  34.  *                Forward declarations            *
  35.  *  ==================================================================    */
  36.  
  37. FLAG delete_text ();
  38. void file_insert ();
  39. void search ();
  40. void re_search ();
  41. void prev_search ();
  42. void search_for ();
  43. void yank ();
  44. FLAG compile ();
  45. int reverse_scroll ();
  46. int find_y ();
  47. int forward_scroll ();
  48. int insert ();
  49. int legal ();
  50. int line_check ();
  51. int check_string ();
  52. int in_list ();
  53. int star ();
  54. FLAG checkmark ();
  55.  
  56. /*  ==================================================================    *
  57.  *                Move Commands                *
  58.  *  ==================================================================    */
  59.  
  60. /*
  61.  * Move one line up.
  62.  */
  63. void
  64. MUP ()
  65. {
  66.   if (hop_flag > 0) HIGH ();
  67.   else if (y == 0) {        /* Top line of screen. Scroll one line */
  68.     if (reverse_scroll (TRUE) != ERRORS) {
  69.         move_y (y);
  70.     }
  71.   }
  72.   else            /* Move to previous line */
  73.     move_y (y - 1);
  74. }
  75.  
  76. /*
  77.  * Move one line down.
  78.  */
  79. void
  80. MDN ()
  81. {
  82.   if (hop_flag > 0) LOW ();
  83.   else if (y == last_y) {    /* Last line of screen. Scroll one line */
  84.     if (bot_line->next == tail && bot_line->text [0] != '\n') {
  85.     /*    dummy_line ();    don't create new empty line ! */
  86.     /*    MDN (); */
  87.         return;
  88.     }
  89.     else {
  90.         (void) forward_scroll (TRUE);
  91.         move_y (y);
  92.     }
  93.   }
  94.   else            /* Move to next line */
  95.     move_y (y + 1);
  96. }
  97.  
  98. /*
  99.  * Move left one position.
  100.  */
  101. void
  102. MLF ()
  103. {
  104.   if (hop_flag > 0) BLINE ();
  105.   else if (x == 0 && get_shift (cur_line->shift_count) == 0) {/* Begin of line */
  106.     if (cur_line->prev != header) {
  107.         MUP ();                    /* Move one line up */
  108.         move_to (LINE_END, y);
  109.     }
  110.   }
  111.   else
  112.     move_to (x - 1, y);
  113. }
  114.  
  115. /*
  116.  * Move right one position.
  117.  */
  118. void
  119. MRT ()
  120. {
  121.   if (hop_flag > 0) ELINE ();
  122.   else if (* cur_text == '\n') {
  123.     if (cur_line->next != tail) {        /* Last char of file */
  124.         MDN ();                /* Move one line down */
  125.         move_to (LINE_START, y);
  126.     }
  127.   }
  128.   else
  129.     move_to (x + 1, y);
  130. }
  131.  
  132. /*
  133.  * Move to top of screen
  134.  */
  135. void
  136. HIGH ()
  137. {
  138.   move_y (0);
  139. }
  140.  
  141. /*
  142.  * Move to bottom of screen
  143.  */
  144. void
  145. LOW ()
  146. {
  147.   move_y (last_y);
  148. }
  149.  
  150. /*
  151.  * Move to begin of line.
  152.  */
  153. void
  154. BLINE ()
  155. {
  156.   move_to (LINE_START, y);
  157. }
  158.  
  159. /*
  160.  * Move to end of line.
  161.  */
  162. void
  163. ELINE ()
  164. {
  165.   move_to (LINE_END, y);
  166. }
  167.  
  168. /*
  169.  * GOTO () prompts for a linenumber and moves to that line.
  170.  */
  171. void
  172. goline (number)
  173.   int number;
  174. {
  175.   LINE * line;
  176.   if (number <= 0 || (line = proceed (header->next, number - 1)) == tail)
  177.     error ("Illegal line number: ", num_out ((long) number));
  178.   else    {
  179.       clear_status ();
  180.       move_y (find_y (line));
  181.     }
  182. }
  183.  
  184. void
  185. goproz (number)
  186.   int number;
  187. {
  188.   goline (number * total_lines / 100);
  189. }
  190.  
  191. void
  192. GOTO ()
  193. {
  194.   uchar c;
  195.   char end;
  196.   int number;
  197.  
  198.   if (! char_ready_within (500)) status_msg ("HOP ... command (fortified) or line number...");
  199.   if (quit == TRUE) return;
  200.   c = readchar ();
  201.   if (quit == TRUE) return;
  202.   if ('0' <= c && c <= '9') {
  203.     end = get_number ("Please continue line number...", c, & number);
  204.     if (end == '%')
  205.         goproz (number);
  206.     else if (end != ERRORS)
  207.         goline (number);
  208.     return;
  209.   }
  210.   else {
  211.     clear_status ();
  212.     hop_flag = 1;
  213.     (* key_map [c]) (c);
  214.     return;
  215.   }
  216. }
  217.  
  218. /*
  219.  * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes
  220.  * top_line of display.) Try to leave the cursor on the same line. If this is
  221.  * not possible, leave cursor on the line halfway the page.
  222.  */
  223. void
  224. PD ()
  225. {
  226.   register int i;
  227.   int new_y;
  228.  
  229.   if (hop_flag > 0) {hop_flag = 0; EFILE (); return;}
  230.  
  231.   for (i = 0; i < SCREENMAX; i ++)
  232.     if (forward_scroll (page_scroll) == ERRORS)
  233.         break;            /* EOF reached */
  234.  
  235.   if (y - i < 0)            /* Line no longer on screen */
  236.     new_y = (page_stay == TRUE) ? 0 : SCREENMAX >> 1;
  237.   else    new_y = y - i;
  238.  
  239.   if (page_scroll == FALSE) display (0, top_line, last_y, new_y);
  240.  
  241.   move_y (new_y);
  242. }
  243.  
  244. /*
  245.  * Scroll backwards one page or to top of file, whatever comes first.
  246.  * (Top_line becomes bot_line of display).
  247.  * The very bottom line (YMAX) is always blank.
  248.  * Try to leave the cursor on the same line.
  249.  * If this is not possible, leave cursor on the line halfway the page.
  250.  */
  251. void
  252. PU ()
  253. {
  254.   register int i;
  255.   int new_y;
  256.  
  257.   if (hop_flag > 0) {hop_flag = 0; BFILE (); return;}
  258.  
  259.   for (i = 0; i < SCREENMAX; i ++) {
  260.     if (reverse_scroll (page_scroll) == ERRORS)
  261.         /* should also flag reverse_scroll that clearing of 
  262.            bottom line is not desired */
  263.         break;            /* Top of file reached */
  264.   }
  265.  
  266.   if (y + i > SCREENMAX)        /* line no longer on screen */
  267.     new_y = (page_stay == TRUE) ? last_y : SCREENMAX >> 1;
  268.   else
  269.     new_y = y + i;
  270.  
  271.   if (can_scroll_reverse == TRUE && page_scroll == TRUE) {
  272.     set_cursor (0, YMAX);    /* Erase very bottom line */
  273.     clear_lastline ();
  274.   }
  275.   else display (0, top_line, last_y, new_y);
  276.  
  277.   move_y (new_y);
  278. }
  279.  
  280. /*
  281.  * Go to top of file, scrolling if possible, else redrawing screen.
  282.  */
  283. void
  284. BFILE ()
  285. {
  286.   if (proceed (top_line, - SCREENMAX) == header)
  287.     PU ();            /* It fits. Let PU do it */
  288.   else {
  289.     reset (header->next, 0);/* Reset top_line, etc. */
  290.     RD_y (0);        /* Display full page */
  291.   }
  292.   move_to (LINE_START, 0);
  293. }
  294.  
  295. /*
  296.  * Go to last position of text, scrolling if possible, else redrawing screen
  297.  */
  298. void
  299. EFILE ()
  300. {
  301.   /* if (tail->prev->text [0] != '\n') dummy_line (); */
  302.   if (proceed (bot_line, SCREENMAX) == tail)
  303.     PD ();            /* It fits. Let PD do it */
  304.   else {
  305.     reset (proceed (tail->prev, - SCREENMAX), SCREENMAX);
  306.     RD_y (last_y);        /* Display full page */
  307.   }
  308.   move_to (LINE_END /* not START ("EFILE"!) */, last_y);
  309. }
  310.  
  311. /*
  312.  * Scroll one line up. Leave the cursor on the same line (if possible).
  313.  */
  314. void
  315. SU ()
  316. {
  317.   register int i;
  318.  
  319.   if (hop_flag > 0) {
  320.     hop_flag = 0;
  321.     for (i = 0; i < (SCREENMAX >> 1); i ++) SU ();
  322.     return;
  323.   }
  324.  
  325.   if (reverse_scroll (TRUE) != ERRORS) {    /* else we are at top of file */
  326.     move_y ((y == SCREENMAX) ? SCREENMAX : y + 1);
  327.   }
  328. }
  329.  
  330. /*
  331.  * Scroll one line down. Leave the cursor on the same line (if possible).
  332.  */
  333. void
  334. SD ()
  335. {
  336.   register int i;
  337.  
  338.   if (hop_flag > 0) {
  339.     hop_flag = 0;
  340.     for (i = 0; i < (SCREENMAX >> 1); i ++) SD ();
  341.     return;
  342.   }
  343.  
  344.   if (forward_scroll (TRUE) != ERRORS)
  345.     move_y ((y == 0) ? 0 : y - 1);
  346. }
  347.  
  348. /*
  349.  * Perform a forward scroll. It returns ERRORS if we're at the last line of
  350.  * the file.
  351.  */
  352. int
  353. forward_scroll (update)
  354.   FLAG update;
  355. {
  356.   if (bot_line->next == tail)        /* Last line of file. No dice */
  357.     return ERRORS;
  358.   top_line = top_line->next;
  359.   bot_line = bot_line->next;
  360.   cur_line = cur_line->next;
  361.  
  362. /* Perform the scroll on screen */
  363.   if (update == TRUE) {
  364.     scroll_forward ();
  365.     set_cursor (0, SCREENMAX);
  366.     line_print (bot_line);
  367.   }
  368.  
  369.   return FINE;
  370. }
  371.  
  372. /*
  373.  * Perform a backwards scroll. It returns ERRORS if we're at the first line 
  374.  * of the file. It updates the display completely if update is TRUE. 
  375.  * Otherwise it leaves that to the caller (page up function).
  376.  */
  377. int
  378. reverse_scroll (update)
  379.   FLAG update;
  380. {
  381.   if (top_line->prev == header)
  382.     return ERRORS;        /* Top of file. Can't scroll */
  383.  
  384.   if (last_y != SCREENMAX)    /* Reset last_y if necessary */
  385.     last_y ++;
  386.   else
  387.     bot_line = bot_line->prev;    /* Else adjust bot_line */
  388.   top_line = top_line->prev;
  389.   cur_line = cur_line->prev;
  390.  
  391. /* Perform the scroll on screen */
  392.   if (update == TRUE)
  393.     if (can_scroll_reverse == TRUE) {
  394.     set_cursor (0, 0);
  395.     scroll_reverse ();
  396.     set_cursor (0, YMAX);    /* Erase very bottom line */
  397.     clear_lastline ();
  398.     set_cursor (0, 0);
  399.     line_print (top_line);
  400.     }
  401.     else display (0, top_line, last_y, y);
  402.  
  403.   return FINE;
  404. }
  405.  
  406. /*----------------------*
  407.  *    Word moves    *
  408.  *----------------------*/
  409.  
  410. /*
  411.  * A word was previously defined as a number of non-blank characters
  412.  * separated by tabs, spaces or linefeeds.
  413.  * By consulting idfchar (), sequences of real letters only or digits
  414.  * or underlines are recognized as words.
  415.  */
  416. extern int idfchar ();
  417.  
  418. /*
  419.  * BSEN () and ESEN () look for the beginning or end of the current sentence.
  420.  */
  421. void
  422. BSEN ()
  423. {
  424.   search_for ("[;.]", REVERSE);
  425. }
  426.  
  427. void
  428. ESEN ()
  429. {
  430.   search_for ("[;.]", FORWARD);
  431. }
  432.  
  433. /*
  434.  * SIDF () searches for the identifier at the current position
  435.  */
  436. void
  437. SIDF (method)
  438.   FLAG method;
  439. {
  440.   char idf_buf [MAX_CHARS];    /* identifier to search for */
  441.   char * idf_buf_poi = idf_buf;
  442.   char * idf_poi;
  443.  
  444.   if (! alpha (* cur_text)) {
  445.     error ("No identifier", NIL_PTR);
  446.     return;
  447.   } else {
  448.     idf_poi = cur_text;
  449.     while (alpha (* idf_poi) && idf_poi != cur_line->text) idf_poi --;
  450.     if (! alpha (* idf_poi)) idf_poi ++;
  451.     while (alpha (* idf_poi)) * idf_buf_poi ++ = * idf_poi ++;
  452.     * idf_buf_poi = '\0';
  453.     search_for (idf_buf, method);
  454.   }
  455. }
  456.  
  457. /*
  458.  * MPW () moves to the start of the previous word. A word is defined as a
  459.  * number of non-blank characters separated by tabs spaces or linefeeds.
  460.  */
  461. void
  462. move_previous_word (remove)
  463.   FLAG remove;
  464. {
  465.   register char * begin_line;
  466.   register char * textp;
  467.   char start_char = * cur_text;
  468.   char * start_pos = cur_text;
  469.   FLAG idfsearch;
  470.  
  471.   if (remove == DELETE && viewonly == TRUE)
  472.     {viewonlyerr (); return;}
  473.  
  474. /* First check if we're at the beginning of line. */
  475.   if (cur_text == cur_line->text) {
  476.     if (cur_line->prev == header)
  477.         return;
  478.     start_char = '\0';
  479.   }
  480.  
  481.   MLF ();
  482.  
  483.   begin_line = cur_line->text;
  484.   textp = cur_text;
  485.  
  486. /* Check if we're in the middle of a word. */
  487.   if (!alpha (* textp) || !alpha (start_char)) {
  488.     while (textp != begin_line && (white_space (* textp) || * textp == '\n'))
  489.         textp --;
  490.   }
  491.  
  492. /* Now we're at the end of previous word. Skip non-blanks until a blank comes */
  493.   if (idfchar (* textp)) {
  494.     idfsearch = TRUE;
  495.     while (textp != begin_line && idfchar (* textp))
  496.         textp --;
  497.   }
  498.   else {
  499.     idfsearch = FALSE;
  500.     while (textp != begin_line && alpha (* textp) && !idfchar (* textp))
  501.         textp --;
  502.   }
  503.  
  504. /* Go to the next char if we're not at the beginning of the line */
  505. /* At the beginning of the line, check whether to stay or to go to the word */
  506.   if (textp != begin_line && * textp != '\n')
  507.     textp ++;
  508.   else if (textp == begin_line && * textp != '\n' &&
  509.        ((idfsearch == TRUE) ? !idfchar (* textp)
  510.         /*    : white_space (* textp) */
  511.             : (!alpha (* textp) || idfchar (* textp)))) {
  512.     textp ++;
  513.     if (white_space (* textp) || textp == start_pos)
  514.         /* no word there or not moved, so go back */
  515.         textp --;
  516.   }
  517.  
  518. /* Find the x-coordinate of this address, and move to it */
  519.   move_address (textp, y);
  520.   if (remove == DELETE)
  521.     (void) delete_text (cur_line, textp, cur_line, start_pos);
  522. }
  523.  
  524. void
  525. MPW ()
  526. {
  527.   if (hop_flag > 0) BSEN ();
  528.   else move_previous_word (NO_DELETE);
  529. }
  530.  
  531. /*
  532.  * MNW () moves to the start of the next word. A word is defined as a number of
  533.  * non-blank characters separated by tabs spaces or linefeeds. Always keep in
  534.  * mind that the pointer shouldn't pass the '\n'.
  535.  */
  536. void
  537. move_next_word (remove)
  538.   FLAG remove;
  539. {
  540.   register char * textp = cur_text;
  541.  
  542.   if (remove == DELETE && viewonly == TRUE)
  543.     {viewonlyerr (); return;}
  544.  
  545. /* Move to the end of the current word. */
  546.   if (idfchar (* textp))
  547.     while (* textp != '\n' && idfchar (* textp))
  548.     textp ++;
  549.   else
  550.     while (alpha (* textp) && !idfchar (* textp))
  551.     textp ++;
  552.  
  553. /* Skip all white spaces */
  554.   while (* textp != '\n' && white_space (* textp))
  555.     textp ++;
  556. /* If we're deleting, delete the text in between */
  557.   if (remove == DELETE) {
  558.     (void) delete_text (cur_line, cur_text, cur_line, textp);
  559.     return;
  560.   }
  561.  
  562. /* If we're at end of line, move to the beginning of (first word on) the next line */
  563.   if (* textp == '\n' && cur_line->next != tail) {
  564.     MDN ();
  565.     move_to (LINE_START, y);
  566.     textp = cur_text;
  567. /*    while (* textp != '\n' && white_space (* textp))    */
  568. /*        textp ++;                    */
  569.   }
  570.   move_address (textp, y);
  571. }
  572.  
  573. void
  574. MNW ()
  575. {
  576.   if (hop_flag > 0) ESEN ();
  577.   else move_next_word (NO_DELETE);
  578. }
  579.  
  580. /*
  581.  * find_y () checks if the matched line is on the current page. If it is, it
  582.  * returns the new y coordinate, else it displays the correct page with the
  583.  * matched line in the middle and returns the new y value;
  584.  */
  585. int
  586. find_y_RD (match_line, redrawflag)
  587.   LINE * match_line;
  588.   FLAG redrawflag;
  589. {
  590.   register LINE * line;
  591.   register int count = 0;
  592.  
  593. /* Check if match_line is on the same page as currently displayed. */
  594.   for (line = top_line; line != match_line && line != bot_line->next;
  595.                               line = line->next)
  596.     count ++;
  597.   if (line != bot_line->next)
  598.     return count;
  599.  
  600. /* Display new page, with match_line in center. */
  601.   if ((line = proceed (match_line, - (SCREENMAX >> 1))) == header) {
  602.   /* Can't display in the middle. Make first line of file top_line */
  603.     count = 0;
  604.     for (line = header->next; line != match_line; line = line->next)
  605.         count ++;
  606.     line = header->next;
  607.   }
  608.   else    /* New page is displayed. Set cursor to middle of page */
  609.     count = SCREENMAX >> 1;
  610.  
  611. /* Reset pointers and redraw the screen */
  612.   reset (line, 0);
  613.   if (redrawflag == TRUE) RD_y (count);
  614.  
  615.   return count;
  616. }
  617.  
  618. int
  619. find_y (match_line)
  620.   LINE * match_line;
  621. {
  622.   return find_y_RD (match_line, TRUE);
  623. }
  624.  
  625. int
  626. find_y_w_o_RD (match_line)
  627.   LINE * match_line;
  628. {
  629.   return find_y_RD (match_line, FALSE);
  630. }
  631.  
  632. /*
  633.  * Dummy_line () adds an empty line at the end of the file. This is 
  634.  * sometimes useful in combination with the EFILE and MDN command in 
  635.  * combination with the Yank command set.
  636.  * !!! I see no use for this and I don't consider such autonomous 
  637.  * !!! modifications of the text (without user request) acceptable. TW.
  638.  */
  639. #ifdef UNUSED
  640. void
  641. dummy_line ()
  642. {
  643.     (void) line_insert (tail->prev, "\n", 1);
  644.     tail->prev->shift_count = DUMMY;
  645.     if (last_y != SCREENMAX) {
  646.         last_y ++;
  647.         bot_line = bot_line->next;
  648.     }
  649. }
  650. #endif /* UNUSED */
  651.  
  652. /*  ==================================================================    *
  653.  *                Modify Commands                *
  654.  *  ==================================================================    */
  655.  
  656. /*
  657.  * DCC deletes the character under the cursor. If this character is a '\n' the
  658.  * current line is joined with the next one.
  659.  * If this character is the only character of the line, the current line will
  660.  * be deleted.
  661.  */
  662. void
  663. DCC ()
  664. {
  665.   if (* cur_text == '\n')
  666.     if (cur_line->next == tail)
  667.        return;
  668.     else
  669.        (void) delete_text (cur_line, cur_text, cur_line->next, cur_line->next->text);
  670.   else {
  671.     if (Chinese == TRUE && multichar (* cur_text))
  672.         (void) delete_text (cur_line, cur_text, cur_line, cur_text + 2);
  673.     else
  674.         (void) delete_text (cur_line, cur_text, cur_line, cur_text + 1);
  675.   }
  676. }
  677.  
  678. /*
  679.  * DPC deletes the character on the left side of the cursor.  If the cursor
  680.  * is at the beginning of the line, the last character if the previous line
  681.  * is deleted. With hop flag, delete left part of line from current point.
  682.  */
  683. void
  684. DPC ()
  685. {
  686.   char * delete_pos;
  687.  
  688.   if (x == 0 && cur_line->prev == header)
  689.     return;            /* Top of file */
  690.  
  691.   if (viewonly == TRUE)
  692.     {viewonlyerr (); return;}
  693.  
  694.   if (hop_flag > 0) {
  695.     hop_flag = 0;
  696.     if (cur_text != cur_line->text) {
  697.       delete_pos = cur_text;
  698.       BLINE ();
  699.       (void) delete_text (cur_line, cur_line->text, cur_line, delete_pos);
  700.     }
  701.   }
  702.   else {
  703.     MLF ();            /* Move one left */
  704.     DCC ();            /* Delete character under cursor */
  705.   }
  706. }
  707.  
  708. /*
  709.  * DLINE delete the whole current line.
  710.  */
  711. void
  712. DLINE ()
  713. {
  714.   if (viewonly == TRUE)
  715.     {viewonlyerr (); return;}
  716.  
  717.   if (hop_flag > 0) {
  718.     hop_flag = 0;
  719.     if (* cur_text != '\n')
  720.     (void) delete_text (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
  721.   }
  722.   else {
  723.     (void) delete_text (cur_line, cur_line->text, cur_line->next, cur_line->next->text);
  724.     BLINE ();
  725.   }
  726. }
  727.  
  728. /*
  729.  * DLN deletes all characters until the end of the line. If the current
  730.  * character is a '\n', then delete that char.
  731.  */
  732. void
  733. DLN ()
  734. {
  735.   if (* cur_text == '\n')
  736.     DCC ();
  737.   else if (hop_flag > 0) {
  738.     hop_flag = 0;
  739.     DLINE ();
  740.   }
  741.   else    (void) delete_text (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
  742. }
  743.  
  744. /*
  745.  * DNW () deletes the next word (as defined in MNW ())
  746.  */
  747. void
  748. DNW ()
  749. {
  750.   if (* cur_text == '\n')
  751.     DCC ();
  752.   else
  753.     move_next_word (DELETE);
  754. }
  755.  
  756. /*
  757.  * DPW () deletes the previous word (as defined in MPW ())
  758.  */
  759. void
  760. DPW ()
  761. {
  762.   if (cur_text == cur_line->text)
  763.     DPC ();
  764.   else
  765.     move_previous_word (DELETE);
  766. }
  767.  
  768. /*
  769.  * Insert character `character' at current location.
  770.  */
  771. void
  772. SNL ()
  773. {
  774.   S ('\n');
  775. }
  776.  
  777. void
  778. S (character)
  779.   register uchar character;
  780. {
  781.   static uchar buffer [3];
  782.   static uchar firstbyte;
  783.   static int width = 1;
  784.  
  785.   if (Chinese == TRUE) {
  786.     if (firstbyte != '\0') {
  787.         buffer [0] = firstbyte;
  788.         buffer [1] = character;
  789.         width = 2;
  790.     } else if (multichar (character)) {
  791.         firstbyte = character;
  792.         return;
  793.     } else {
  794.         buffer [0] = character;
  795.         buffer [1] = '\0';
  796.         width = 1;
  797.     }
  798.     firstbyte = '\0';
  799.   } else
  800.     buffer [0] = character;
  801.  
  802. /* Insert the character */
  803.   if (insert (cur_line, cur_text, buffer) == ERRORS)
  804.     return;
  805.  
  806. /* Fix screen */
  807.   if (character == '\n') {
  808.     set_cursor (0, y);
  809.     if (y == SCREENMAX) {        /* Can't use display () */
  810.         line_print (cur_line);
  811.         (void) forward_scroll (TRUE);
  812.         move_to (0, y);
  813.     }
  814.     else {
  815.         reset (top_line, y);    /* Reset pointers */
  816.         if (can_add_line == TRUE) {
  817.             add_line (y + 1);
  818.             clear_status ();
  819.             display (y, cur_line, 1, y + 1);
  820.         }
  821.         else    display (y, cur_line, last_y - y, y + 1);
  822.         move_to (0, y + 1);
  823.     }
  824.   }
  825.   else if (x + width == XBREAK) /* If line must be shifted, just call move_to */
  826.     move_to (x + width, y);
  827.   else {            /* else display rest of line */
  828.     put_line (cur_line, x, FALSE, FALSE);
  829.     move_to (x + width, y);
  830.   }
  831. }
  832.  
  833. /*
  834.  * Replace current character with its hex representation.
  835.  */
  836. uchar hexdig (c)
  837.   uchar c;
  838. {
  839.   if (c < 10) return c + '0';
  840.     else  return c - 10 + 'A';
  841. }
  842. void
  843. insertcode (c, radix)
  844.   uchar c;
  845.   int radix;
  846. {
  847.   int radix2;
  848.  
  849.   if (radix == 8) {
  850.     S (hexdig ((c >> 6) & 007));
  851.     S (hexdig ((c >> 3) & 007));
  852.     S (hexdig ((c) & 007));
  853.   } else if (radix == 16) {
  854.     S (hexdig ((c >> 4) & 017));
  855.     S (hexdig ((c) & 017));
  856.   } else {    /* assume radix = 10 or, at least, three digits suffice */
  857.     radix2 = radix * radix;
  858.     S (hexdig (c / radix2));
  859.     S (hexdig ((c % radix2) / radix));
  860.     S (hexdig (c % radix));
  861.   }
  862. }
  863. void
  864. changetocode (radix)
  865.   int radix;
  866. {
  867.   uchar c = * cur_text;
  868.  
  869.   if (c == '\n') {
  870. #ifdef msdos
  871.     insertcode ('\r', radix);
  872. #endif
  873.     insertcode ('\n', radix);
  874.   } else {
  875.     DCC ();
  876.     insertcode (c, radix);
  877.   }
  878. }
  879.  
  880. /*
  881.  * insert_accent inserts accented character
  882.  */
  883. void
  884. insert_accent (name, routine)
  885.   char * name;
  886.   uchar (* routine) ();
  887. {
  888.   register uchar letter;
  889.  
  890.   build_string (text_buffer, "Enter character to place %s on...", name);
  891.   status_msg (text_buffer);
  892.   letter = (* routine) (readchar ());
  893.   clear_status ();
  894.   S (letter);
  895. }
  896.  
  897. /*
  898.  * CTRl inserts a control-char at the current location. A message that this
  899.  * function is called is displayed at the status line.
  900.  */
  901. void
  902. CTRl ()
  903. {
  904.   register uchar ctrl;
  905.  
  906.   status_msg ("Enter control character (or accent)...");
  907.   ctrl = readchar ();
  908.   if (ctrl == ring || ctrl == ',')
  909.     {insert_accent ("angstrom/cedilla", angstrom); return;}
  910.   else switch (ctrl) {
  911.     case '"':    {insert_accent ("diaeresis", diaeresis); return;}
  912.     case '\'':    {insert_accent ("acute (d'aigu)", acute); return;}
  913.     case '`':    {insert_accent ("grave", grave); return;}
  914.     case '^':    {insert_accent ("circumflex", circumflex); return;}
  915.     case '~':    {insert_accent ("tilde", tilde); return;}
  916.   }
  917.   clear_status ();
  918.   if ((ctrl == '\177') || (ctrl == '?')) {S ('\177'); return;}
  919.   ctrl = ctrl & '\237';
  920.   if (ctrl == '\0') error ("Can't handle NULL char - not inserted", NIL_PTR);
  921.   else S (ctrl);
  922. }
  923.  
  924. /*
  925.  * LIB insert a line at the current position and moves back to the end of
  926.  * the previous line.
  927.  */
  928. void
  929. LIB ()
  930. {
  931.   hop_flag = 0;
  932.  
  933.   if (viewonly == TRUE)
  934.     {viewonlyerr (); return;}
  935.  
  936.   S ('\n');            /* Insert the line */
  937.   MUP ();            /* Move one line up */
  938.   move_to (LINE_END, y);    /* Move to end of this line */
  939. }
  940.  
  941. /*  ==================================================================    *
  942.  *                Yank Commands                *
  943.  *  ==================================================================    */
  944.  
  945. LINE * mark_line = NIL_LINE;        /* For marking position. */
  946. char * mark_text = NIL_PTR;
  947. LINE * mark_n_line [10] = {NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, 
  948.                NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE};
  949. char * mark_n_text [10] = {NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, 
  950.                NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR};
  951. int lines_saved;            /* Nr of lines in buffer */
  952.  
  953. /*
  954.  * PT () inserts the buffer at the current location.
  955.  */
  956. void
  957. PT ()
  958. {
  959.   register int fd;        /* File descriptor for buffer */
  960.  
  961.   if (viewonly == TRUE)
  962.     {viewonlyerr (); return;}
  963.  
  964.   if (hop_flag > 0) {
  965.     if ((fd = open (yankie_file, O_RDONLY | O_BINARY, 0)) < 0) {
  966.         error ("No inter window buffer present", NIL_PTR);
  967.         return;
  968.     }
  969.   }
  970.   else if ((fd = scratch_file (READ, FALSE)) == ERRORS) {
  971.     error ("Buffer is empty", NIL_PTR);
  972.     return;
  973.   }
  974.   /* Insert the buffer */
  975. /*    file_insert (fd, FALSE); => positioning error when TAB in last line */
  976.     file_insert (fd, TRUE);
  977. }
  978.  
  979. /*
  980.  * INSFILE () prompt for a filename and inserts the file at the current location
  981.  * in the file.
  982.  */
  983. void
  984. INSFILE ()
  985. {
  986.   register int fd;        /* File descriptor of file */
  987.   char name [maxLINE_LEN];    /* Buffer for file name */
  988.  
  989.   if (viewonly == TRUE)
  990.     {viewonlyerr (); return;}
  991.  
  992. /* Get the file name */
  993.   if (get_file ("Get and insert file:", name) != FINE)
  994.     return;
  995.   clear_status ();
  996.  
  997.   if ((fd = open (name, O_RDONLY | O_BINARY, 0)) < 0)
  998.     error ("Cannot open file: " /*, name */, serror ());
  999.   else {    /* Insert the file */
  1000.     file_insert (fd, TRUE);    /* leave cursor at begin of insertion */
  1001.   }
  1002. }
  1003.  
  1004. /*
  1005.  * File_insert () inserts the contents of an opened file (as given by
  1006.  * filedescriptor fd) at the current location.
  1007.  * After the insertion, if old_pos is TRUE, the cursor remains at the
  1008.  * start of the inserted text, if old_pos is FALSE, it is placed to
  1009.  * its end. If old_pos is FALSE, this works erroneously if the last line
  1010.  * inserted contains a TAB character!!
  1011.  */
  1012. void
  1013. file_insert (fd, old_pos)
  1014.   int fd;
  1015.   FLAG old_pos;
  1016. {
  1017.   char line_buffer [MAX_CHARS];        /* Buffer for next line */
  1018.   register LINE * line = cur_line;
  1019.   register int line_count = total_lines;    /* Nr of lines inserted */
  1020.   LINE * page = cur_line;
  1021.   int ret = ERRORS;
  1022.  
  1023.   get_l_err1 = NIL_PTR;
  1024.   get_l_err2 = NIL_PTR;
  1025.  
  1026. /* Get the first piece of text (might be ended with a '\n') from fd */
  1027.   if (get_line (fd, line_buffer) == ERRORS)
  1028.     return;                /* Empty file */
  1029.  
  1030. /* Insert this text at the current location */
  1031.   if (insert (line, cur_text, line_buffer) == ERRORS)
  1032.     return;
  1033.  
  1034. /* Repeat getting lines (and inserting lines) until EOF is reached */
  1035.   while (line != NIL_LINE
  1036.      && (ret = get_line (fd, line_buffer)) != ERRORS && ret != NO_LINE)
  1037.     line = line_insert (line, line_buffer, ret);
  1038.  
  1039.   if (line == NIL_LINE) sleep (2) /* show memory allocation error msg */;
  1040.   else if (ret == NO_LINE) {    /* Last line read not ended by a '\n' */
  1041.     line = line->next;
  1042.     if (insert (line, line->text, line_buffer) == ERRORS)
  1043.         sleep (2) /* give time to read error msg */;
  1044.   }
  1045.  
  1046.   (void) close (fd);
  1047.  
  1048. /* If illegal lines were input, report */
  1049.   if ((get_l_err1 != NIL_PTR) || (get_l_err2 != NIL_PTR)) {
  1050.     ring_bell ();
  1051.     error (get_l_err1, get_l_err2);
  1052.     sleep (1);
  1053.   }
  1054.  
  1055. /* Calculate nr of lines added */
  1056.   line_count = total_lines - line_count;
  1057.  
  1058. /* Fix the screen */
  1059.   if (line_count == 0) {        /* Only one line changed */
  1060.     set_cursor (0, y);
  1061.     line_print (line);
  1062.     move_to ((old_pos == TRUE) ? x : x + length_of (line_buffer), y);
  1063.   }
  1064.   else {                /* Several lines changed */
  1065.     reset (top_line, y);    /* Reset pointers */
  1066.     while (page != line && page != bot_line->next)
  1067.         page = page->next;
  1068.     if (page != bot_line->next || old_pos == TRUE)
  1069.         display (y, cur_line, SCREENMAX - y, y);
  1070.         /* screen display style parameter (last) may be inaccurate */
  1071.     if (old_pos == TRUE)
  1072.         move_to (x, y);
  1073.     else if (ret == NO_LINE)
  1074.         move_to (length_of (line_buffer), find_y (line));
  1075.     else
  1076.         move_to (0, find_y (line->next));
  1077.   }
  1078.  
  1079. /* If nr of added line >= REPORT, print the count */
  1080.   if (line_count >= REPORT)
  1081.     status_line (num_out ((long) line_count), " lines added");
  1082. }
  1083.  
  1084. /*
  1085.  * WB () writes the buffer (yank_file) into another file, which
  1086.  * is prompted for.
  1087.  */
  1088. void
  1089. WB ()
  1090. {
  1091.   register int new_fd;        /* Filedescriptor to copy file */
  1092.   int yank_fd;            /* Filedescriptor to buffer */
  1093.   register int cnt;        /* Count check for read/write */
  1094.   int ret = FINE;        /* Error check for write */
  1095.   char file_name [maxLINE_LEN];    /* Output file name */
  1096.   char * msg_doing; char * msg_done;
  1097.  
  1098. /* Checkout the buffer */
  1099.   if ((yank_fd = scratch_file (READ, FALSE)) == ERRORS) {
  1100.     error ("Buffer is empty", NIL_PTR);
  1101.     return;
  1102.   }
  1103.  
  1104. /* Get file name */
  1105.   if (get_file ((hop_flag > 0) ? "Append buffer to file:"
  1106.                    : "Write buffer to file:", file_name) != FINE)
  1107.     return;
  1108.  
  1109. /* Create the new file or open previous file for appending */
  1110.   if (hop_flag > 0) {
  1111.     if ((new_fd = open (file_name, O_WRONLY | O_CREAT | O_APPEND | O_BINARY, fprot)) < 0) {
  1112.     error ("Cannot append to file: ", serror ());
  1113.     return;
  1114.     }
  1115.     msg_doing = "Appending "; msg_done = "Appended";
  1116.   }
  1117.   else {
  1118.     if (checkoverwrite (file_name) != TRUE)
  1119.     return;
  1120.     else if ((new_fd = open (file_name, O_WRONLY | O_CREAT | O_BINARY, fprot)) < 0) {
  1121.     error ("Cannot create file: ", serror ());
  1122.     return;
  1123.     }
  1124.     msg_doing = "Writing "; msg_done = "Wrote";
  1125.   }
  1126.  
  1127.   status_line (msg_doing, file_name);
  1128.  
  1129. /* Copy buffer into file */
  1130.   while ((cnt = read (yank_fd, text_buffer, sizeof (text_buffer))) > 0)
  1131.     if (write (new_fd, text_buffer, cnt) != cnt) {
  1132.         bad_write (new_fd);
  1133.         ret = ERRORS;
  1134.         break;
  1135.     }
  1136.  
  1137. /* Clean up open files and status_line */
  1138.   (void) close (new_fd);
  1139.   (void) close (yank_fd);
  1140.  
  1141.   if (ret != ERRORS)            /* Bad write */
  1142.     file_status (msg_done, chars_saved, file_name, lines_saved, 
  1143.             FALSE, TRUE, FALSE, FALSE);
  1144. }
  1145.  
  1146. /*
  1147.  * MARK sets mark_line / mark_text to the current line / current text pointer.
  1148.  */
  1149. void
  1150. MARK ()
  1151. {
  1152.   if (hop_flag > 0) GOMA ();
  1153.   else {
  1154.     mark_line = cur_line;
  1155.     mark_text = cur_text;
  1156.     status_msg ("Mark set");
  1157.   }
  1158. }
  1159.  
  1160. /*
  1161.  * GOMA moves to the marked position
  1162.  */
  1163. void
  1164. GOMA ()
  1165. {
  1166.   if (checkmark (mark_line, mark_text) == NOT_VALID)
  1167.     error ("Mark not set", NIL_PTR);
  1168.   else
  1169.     move_address (mark_text, find_y (mark_line));
  1170. }
  1171.  
  1172. /*
  1173.  * MARKn sets mark n to the current line / current text pointer.
  1174.  */
  1175. void
  1176. MARKn (c)
  1177.   uchar c;
  1178. {
  1179.   if (hop_flag > 0) GOMAn (c);
  1180.   else {
  1181.     mark_n_line [c & '\17'] = cur_line;
  1182.     mark_n_text [c & '\17'] = cur_text;
  1183.     status_msg ("Mark set");
  1184.   }
  1185. }
  1186.  
  1187. /*
  1188.  * GOMAn moves to the marked position n
  1189.  */
  1190. void
  1191. GOMAn (c)
  1192.   uchar c;
  1193. {
  1194.   if (checkmark (mark_n_line [c & '\17'], mark_n_text [c & '\17']) == NOT_VALID)
  1195.     error ("Mark not set", NIL_PTR);
  1196.   else
  1197.     move_address (mark_n_text [c & '\17'], find_y (mark_n_line [c & '\17']));
  1198. }
  1199.  
  1200. /*
  1201.  * Yankie () provides a reference to the last saved buffer to be read
  1202.  * by other mined invocations.
  1203.  */
  1204. void
  1205. yankie ()
  1206. {
  1207.   delete_file (yankie_file);
  1208. #ifdef unix
  1209.   link (yank_file, yankie_file);
  1210. #else
  1211.   build_string (text_buffer, copycommand, yank_file, yankie_file);
  1212.   system (text_buffer);
  1213. #endif
  1214. }
  1215.  
  1216. /*
  1217.  * Set_up is an interface to the actual yank. It calls checkmark () to check
  1218.  * if the marked position is still valid. If it is, yank is called with the
  1219.  * arguments in the right order.
  1220.  */
  1221. void
  1222. set_up (remove, append)
  1223.   FLAG remove;    /* == DELETE if text should be deleted */
  1224.   FLAG append;    /* == TRUE if text should only be appended to yank buffer */
  1225. {
  1226.   switch (checkmark (mark_line, mark_text)) {
  1227.     case NOT_VALID :
  1228.         error ("Mark not set", NIL_PTR);
  1229.         return;
  1230.     case SMALLER :
  1231.         yank (mark_line, mark_text, cur_line, cur_text, remove, append);
  1232.         yankie ();
  1233.         break;
  1234.     case BIGGER :
  1235.         yank (cur_line, cur_text, mark_line, mark_text, remove, append);
  1236.         yankie ();
  1237.         break;
  1238.     case SAME :        /* Ignore stupid behaviour */
  1239.     /*    yank_status = EMPTY;    */
  1240.         chars_saved = 0L;
  1241.         status_msg ("Nothing to save");
  1242.         break;
  1243.     default :
  1244.         error ("Internal mark error", NIL_PTR);
  1245.         return;
  1246.   }
  1247. }
  1248.  
  1249. /*
  1250.  * YA () puts the text between the marked position and the current
  1251.  * in the buffer.
  1252.  */
  1253. void
  1254. YA ()
  1255. {
  1256.   set_up (NO_DELETE, (hop_flag > 0) ? TRUE : FALSE);
  1257. }
  1258.  
  1259. /*
  1260.  * DT () is essentially the same as YA (), but in DT () the text is deleted.
  1261.  */
  1262. void
  1263. DT ()
  1264. {
  1265.   if (viewonly == TRUE)
  1266.     {viewonlyerr (); return;}
  1267.  
  1268.   set_up (DELETE, (hop_flag > 0) ? TRUE : FALSE);
  1269. }
  1270.  
  1271. /*
  1272.  * Check_mark () checks if mark_line and mark_text are still valid pointers.
  1273.  * If they are it returns
  1274.  * SMALLER if the marked position is before the current,
  1275.  * BIGGER if it isn't or SAME if somebody didn't get the point.
  1276.  * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
  1277.  * Legal () checks if mark_text is valid on the mark_line.
  1278.  */
  1279. FLAG
  1280. checkmark (mark_line, mark_text)
  1281.   register LINE * mark_line;
  1282.   register char * mark_text;
  1283. {
  1284.   register LINE * line;
  1285.   FLAG cur_seen = FALSE;
  1286.  
  1287. /* Special case: check is mark_line and cur_line are the same. */
  1288.   if (mark_line == cur_line) {
  1289.     if (mark_text == cur_text)    /* Even same place */
  1290.         return SAME;
  1291.     if (legal (mark_line, mark_text) == ERRORS) /* mark_text out of range */
  1292.         return NOT_VALID;
  1293.     return (mark_text < cur_text) ? SMALLER : BIGGER;
  1294.   }
  1295.  
  1296. /* Start looking for mark_line in the line structure */
  1297.   for (line = header->next; line != tail; line = line->next) {
  1298.     if (line == cur_line)
  1299.         cur_seen = TRUE;
  1300.     else if (line == mark_line)
  1301.         break;
  1302.   }
  1303.  
  1304. /* If we found mark_line (line != tail) check for legality of mark_text */
  1305.   if (line == tail || legal (mark_line, mark_text) == ERRORS)
  1306.     return NOT_VALID;
  1307.  
  1308. /* cur_seen is TRUE if cur_line is before mark_line */
  1309.   return (cur_seen == TRUE) ? BIGGER : SMALLER;
  1310. }
  1311.  
  1312. /*
  1313.  * Legal () checks if mark_text is still a valid pointer.
  1314.  */
  1315. int
  1316. legal (mark_line, mark_text)
  1317.   register LINE * mark_line;
  1318.   register char * mark_text;
  1319. {
  1320.   register char * textp = mark_line->text;
  1321.  
  1322. /* Locate mark_text on mark_line */
  1323.   while (textp != mark_text && * textp != '\0')
  1324.     textp ++;
  1325.   return (* textp == '\0') ? ERRORS : FINE;
  1326. }
  1327.  
  1328. /*
  1329.  * Yank puts all the text between start_position and end_position into
  1330.  * the buffer.
  1331.  * The caller must check that the arguments to yank () are valid (e.g. in
  1332.  * the right order).
  1333.  */
  1334. void
  1335. yank (start_line, start_textp, end_line, end_textp, remove, append)
  1336.   LINE * start_line, * end_line;
  1337.   char * start_textp, * end_textp;
  1338.   FLAG remove;    /* == DELETE if text should be deleted */
  1339.   FLAG append;    /* == TRUE if text should only be appended to yank buffer */
  1340. {
  1341.   register LINE * line = start_line;
  1342.   register char * textp = start_textp;
  1343.   int fd;
  1344.  
  1345. /* Create file to hold buffer */
  1346.   if ((fd = scratch_file (WRITE, append)) == ERRORS)
  1347.     return;
  1348.  
  1349.   chars_saved = 0L;
  1350.   lines_saved = 0;
  1351.   if (append == TRUE)
  1352.     status_msg ("Appending text ...");
  1353.   else    status_msg ("Saving text ...");
  1354.  
  1355. /* Keep writing chars until the end_location is reached. */
  1356.   while (textp != end_textp) {
  1357.     if (writechar (fd, * textp) == ERRORS) {
  1358.         (void) close (fd);
  1359.         return;
  1360.     }
  1361.     if (* textp ++ == '\n') {    /* Move to the next line */
  1362.         line = line->next;
  1363.         textp = line->text;
  1364.         lines_saved ++;
  1365.     }
  1366.     chars_saved ++;
  1367.   }
  1368.  
  1369. /* Flush the I/O buffer and close file */
  1370.   if (flush_buffer (fd) == ERRORS) {
  1371.     (void) close (fd);
  1372.     return;
  1373.   }
  1374.   (void) close (fd);
  1375.   yank_status = VALID;
  1376.  
  1377.   /*
  1378.    * Check if the text should be deleted as well. In case it should,
  1379.    * the following hack is used to save a lot of code.
  1380.    * First move back to the start_position (this might be the current
  1381.    * location) and then delete the text.
  1382.    * This might look a bit confusing to the user the first time.
  1383.    * Delete () will fix the screen.
  1384.    */
  1385.   if (remove == DELETE) {
  1386.     move_to (find_x (start_line, start_textp), find_y (start_line));
  1387.     if (delete_text (start_line, start_textp, end_line, end_textp)
  1388.         == ERRORS) {
  1389.         sleep (2) /* give time to read allocation error msg */;
  1390.     }
  1391.     mark_line = cur_line;
  1392.     mark_text = cur_text;
  1393.   }
  1394.  
  1395.   if (append == TRUE)
  1396.     status_line (num_out (chars_saved), " characters appended to buffer");
  1397.   else    status_line (num_out (chars_saved), " characters saved in buffer");
  1398. }
  1399.  
  1400. /*
  1401.  * Scratch_file () tries to create a unique file in a temporary directory.
  1402.  * It tries several different filenames until one can be created
  1403.  * or MAXTRIALS attempts have been made.
  1404.  * After MAXTRIALS times, an error message is given and ERRORS is returned.
  1405.  */
  1406.  
  1407. #define MAXTRIALS 99
  1408.  
  1409. int
  1410. scratch_file (mode, append)
  1411.   FLAG mode;    /* Can be READ or WRITE permission */
  1412.   FLAG append;    /* == TRUE if text should only be appended to yank buffer */
  1413. {
  1414.   static int trials = 0;    /* Keep track of trials */
  1415.   int fd = 0;            /* Filedescriptor to buffer */
  1416.  
  1417. /* If yank_status == NOT_VALID, scratch_file is called for the first time */
  1418.   if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */
  1419.     /* Generate file name. */
  1420. #ifdef msdos
  1421.     build_string (yank_file, "%s%d", yankie_file, trials);
  1422. #else
  1423.     build_string (yank_file, "%s%d-%d", yankie_file, getpid (), trials);
  1424. #endif
  1425.     /* Check file existence */
  1426.     if (access (yank_file, 0 /* F_OK */) == 0
  1427.         || (fd = creat (yank_file, bufprot)) < 0) {
  1428.         if (++ trials >= MAXTRIALS) {
  1429.             build_string (text_buffer, "Unable to create scratchfile %s: ", yank_file);
  1430.             if (fd == 0)
  1431.             error (text_buffer, "File exists");
  1432.             else
  1433.             error (text_buffer, serror ());
  1434.             return ERRORS;
  1435.         }
  1436.         else
  1437.             return scratch_file (mode, append);    /* try again */
  1438.     }
  1439.   }
  1440.   else if (yank_status == NOT_VALID && mode == READ) {
  1441.     return ERRORS;
  1442.   }
  1443.   else /* yank_status == VALID */
  1444.     if (  (mode == READ && (fd = open (yank_file, O_RDONLY | O_BINARY, 0)) < 0)
  1445.        || (mode == WRITE &&
  1446.         (fd = open (yank_file, O_WRONLY | O_CREAT |
  1447.             ((append == TRUE) ? O_APPEND : O_TRUNC), bufprot)) < 0)) {
  1448.     yank_status = NOT_VALID;
  1449.     return ERRORS;
  1450.   }
  1451.  
  1452.   clear_buffer ();
  1453.   return fd;
  1454. }
  1455.  
  1456. /*  ==================================================================    *
  1457.  *                Search Commands                *
  1458.  *  ==================================================================    */
  1459.  
  1460. /*
  1461.  * A regular expression consists of a sequence of:
  1462.  *    1. A normal character matching that character.
  1463.  *    2. A . matching any character.
  1464.  *    3. A ^ matching the begin of a line.
  1465.  *    4. A $ (as last character of the pattern) mathing the end of a line.
  1466.  *    5. A \<character> matching <character>.
  1467.  *    6. A number of characters enclosed in [] pairs matching any of these
  1468.  *       characters. A list of characters can be indicated by a '-'. So
  1469.  *       [a-z] matches any letter of the alphabet. If the first character
  1470.  *       after the '[' is a '^' then the set is negated (matching none of
  1471.  *       the characters).
  1472.  *       A ']', '^' or '-' can be escaped by putting a '\' in front of it.
  1473.  *    7. If one of the expressions as described in 1-6 is followed by a
  1474.  *       '*' than that expressions matches a sequence of 0 or more of
  1475.  *       that expression.
  1476.  */
  1477.  
  1478. char typed_expression [maxLINE_LEN];    /* Holds previous search expression */
  1479.  
  1480. /*
  1481.  * SFW searches forward for an expression.
  1482.  */
  1483. void
  1484. SFW ()
  1485. {
  1486.   if (hop_flag > 0) SIDF (FORWARD);
  1487.   else search ("Search forward:", FORWARD);
  1488. }
  1489.  
  1490. /*
  1491.  * SRV searches backwards for an expression.
  1492.  */
  1493. void
  1494. SRV ()
  1495. {
  1496.   if (hop_flag > 0) SIDF (REVERSE);
  1497.   else search ("Search reverse:", REVERSE);
  1498. }
  1499.  
  1500. /*
  1501.  * RS searches using the last search direction and expression.
  1502.  */
  1503. void
  1504. RS ()
  1505. {
  1506.   if (hop_flag > 0) prev_search ();
  1507.   else re_search ();
  1508. }
  1509.  
  1510. /*
  1511.  * Get_expression () prompts for an expression. If just a return is typed, the
  1512.  * old expression is used. If the expression changed, compile () is called and
  1513.  * the returning REGEX structure is returned. It returns NIL_REG upon error.
  1514.  * The save flag indicates whether the expression should be appended at the
  1515.  * message pointer.
  1516.  */
  1517. char exp_buf [maxLINE_LEN];        /* Buffer for new expr. */
  1518.  
  1519. REGEX *
  1520. get_expression (message)
  1521.   char * message;
  1522. {
  1523.   static REGEX program;            /* Program of expression */
  1524.  
  1525.   if (get_string (message, exp_buf, FALSE) == ERRORS)
  1526.     return NIL_REG;
  1527.  
  1528.   if (exp_buf [0] == '\0' && typed_expression [0] == '\0') {
  1529.     error ("No previous search expression", NIL_PTR);
  1530.     return NIL_REG;
  1531.   }
  1532.  
  1533.   if (exp_buf [0] != '\0') {            /* A new expr. is typed */
  1534.     copy_string (typed_expression, exp_buf);    /* Save expr. */
  1535.     /* Compile new expression: */
  1536.     if (compile (exp_buf, & program) == ERRORS)
  1537.         return NIL_REG;
  1538.   }
  1539.  
  1540.   if (program.status == REG_ERROR) {    /* Error during compiling */
  1541.     error (program.result.err_mess, NIL_PTR);
  1542.     return NIL_REG;
  1543.   }
  1544.   return & program;
  1545. }
  1546.  
  1547. /*
  1548.  * make_expression is only called by search_for and maintains its own, 
  1549.  * independant search program buffer
  1550.  */
  1551. REGEX *
  1552. make_expression (expr)
  1553.   char * expr;
  1554. {
  1555.   static REGEX program;            /* Program of expression */
  1556.  
  1557.   if (compile (expr, & program) == ERRORS)    /* Compile new expression */
  1558.     return NIL_REG;
  1559.  
  1560.   if (program.status == REG_ERROR) {    /* Error during compiling */
  1561.     error (program.result.err_mess, NIL_PTR);
  1562.     return NIL_REG;
  1563.   }
  1564.   return & program;
  1565. }
  1566.  
  1567. /*
  1568.  * Change () prompts for an expression and a substitution pattern and changes
  1569.  * all matches of the expression into the substitution.
  1570.  * change () starts looking for expressions at the current line and
  1571.  * continues until the end of the file if the FLAG `global' is VALID.
  1572.  * It prompts for each change if the FLAG `confirm' is TRUE.
  1573.  * For a call graph of search procedures see search ().
  1574.  */
  1575. void
  1576. change (message, global, confirm)
  1577.   char * message;        /* Message to prompt for expression */
  1578.   FLAG global, confirm;
  1579. {
  1580.   char mess_buf [maxLINE_LEN];        /* Buffer to hold message */
  1581.   char replacement [maxLINE_LEN];    /* Buffer to hold subst. pattern */
  1582.   REGEX * program;            /* Program resulting from compilation */
  1583.   register LINE * line = cur_line;
  1584.   register char * textp;
  1585.   char * substitute ();
  1586.   long lines = 0L;        /* Nr of lines on which subs occurred */
  1587.   long subs = 0L;        /* Nr of subs made */
  1588.   int ly = y;            /* Index to check if line is on screen */
  1589.   int previousy = y;
  1590.   char c;
  1591.   FLAG quit_change;
  1592.  
  1593.   if (viewonly == TRUE)
  1594.     {viewonlyerr (); return;}
  1595.  
  1596. /* Save message and get expression */
  1597.   if ((program = get_expression (message)) == NIL_REG)
  1598.     return;
  1599.  
  1600. /* Get substitution pattern */
  1601.   build_string (mess_buf, "%s %s by:", message, typed_expression);
  1602.   if (get_string (mess_buf, replacement, FALSE) == ERRORS)
  1603.     return;
  1604.  
  1605.   set_cursor (0, YMAX);
  1606.   flush ();
  1607. /* Substitute until end of file */
  1608.   do {
  1609.     if (line_check (program, line->text, FORWARD)) {
  1610.         lines ++;
  1611.         /* Repeat sub. on this line as long as we find a match */
  1612.         do {
  1613.         if (confirm == TRUE) {
  1614.             ly = find_y (line);
  1615.             textp = program->start_ptr;
  1616.             move_address (program->start_ptr, ly);
  1617.             status_msg ("Replace ? (y/n)");
  1618.             c = promptyn ();
  1619.             clear_status ();
  1620.             if (c == 'y') {
  1621.                 subs ++;    /* Increment subs */
  1622.                 if ((textp = substitute (line, program, replacement))
  1623.                             == NIL_PTR)
  1624.                     return;    /* Line too long */
  1625.                 set_cursor (0, ly);
  1626.                 line_print (line);
  1627.             }
  1628.             else
  1629.                 textp ++;
  1630.         }
  1631.         else {
  1632.             subs ++;    /* Increment subs */
  1633.             line->shift_count = 0;
  1634.             /* in case line would get completely shifted out */
  1635.             if ((textp = substitute (line, program, replacement))
  1636.                             == NIL_PTR) {
  1637.             set_cursor (0, ly);
  1638.             line_print (line);
  1639.             move_to (x, y);
  1640.             return;    /* Line too long */
  1641.             }
  1642.         }
  1643.         } while (  (program->status & BEGIN_LINE) != BEGIN_LINE
  1644.             && (program->status & END_LINE) != END_LINE
  1645.             && line_check (program, textp, FORWARD)
  1646.             && quit == FALSE);
  1647.         /* Check to see if we can print the result */
  1648.         if (confirm == FALSE && ly <= SCREENMAX) {
  1649.         set_cursor (0, ly);
  1650.         line_print (line);
  1651.         }
  1652.     }
  1653.     if (ly <= SCREENMAX)
  1654.         ly ++;
  1655.     line = line->next;
  1656.   } while (line != tail && global == VALID && quit == FALSE);
  1657.  
  1658.   quit_change = quit;
  1659. /* Fix the status line */
  1660.   if (subs == 0L && quit == FALSE)
  1661.     error ("Pattern not found", NIL_PTR);
  1662.   else if (lines >= REPORT || quit == TRUE) {
  1663.     build_string (mess_buf, "%s %ld substitutions on %ld lines",
  1664.         (quit_change == TRUE) ? "(Aborted) " : "", subs, lines);
  1665.     status_msg (mess_buf);
  1666.   }
  1667.   else if (global == NOT_VALID && subs >= REPORT)
  1668.     status_line (num_out (subs), " substitutions");
  1669.   else
  1670.     clear_status ();
  1671.  
  1672.   if (confirm == TRUE) move_to (x, y);
  1673.   else move_to (LINE_START, previousy);
  1674.  
  1675. /*  if (quit == TRUE) swallow_dummy_quit_char (); */
  1676.   quit = FALSE;
  1677. }
  1678.  
  1679. /*
  1680.  * Substitute () replaces the match on this line by the substitute pattern
  1681.  * as indicated by the program. Every '&' in the replacement is replaced by
  1682.  * the original match. A \ in the replacement escapes the next character.
  1683.  */
  1684. char *
  1685. substitute (line, program, replacement)
  1686.   LINE * line;
  1687.   REGEX * program;
  1688.   char * replacement;    /* Contains replacement pattern */
  1689. {
  1690.   register char * textp = text_buffer;
  1691.   register char * subp = replacement;
  1692.   char * linep = line->text;
  1693.   char * amp;
  1694.   char * newtext;
  1695.  
  1696.   modified = TRUE;
  1697.  
  1698. /* Copy part of line until the beginning of the match */
  1699.   while (linep != program->start_ptr)
  1700.     * textp ++ = * linep ++;
  1701.  
  1702. /*
  1703.  * Replace the match by the substitution pattern. Each occurrence of '&' is
  1704.  * replaced by the original match. A \ escapes the next character.
  1705.  */
  1706.   while (* subp != '\0' && textp < & text_buffer [MAX_CHARS]) {
  1707.     if (* subp == '&') {        /* Replace the original match */
  1708.         amp = program->start_ptr;
  1709.         while (amp < program->end_ptr && textp < & text_buffer [MAX_CHARS])
  1710.             * textp ++ = * amp ++;
  1711.         subp ++;
  1712.     }
  1713.     else {
  1714.         if (* subp == '\\' && * (subp + 1) != '\0')
  1715.             subp ++;
  1716.         * textp ++ = * subp ++;
  1717.     }
  1718.   }
  1719.   * textp = '\0';
  1720.  
  1721. /* Check for line length not exceeding MAX_CHARS */
  1722.   if (length_of (text_buffer) + length_of (program->end_ptr) >= MAX_CHARS) {
  1723.     error ("Substitution failed: resulted line too long", NIL_PTR);
  1724.     return NIL_PTR;
  1725.   }
  1726.  
  1727. /* Append last part of line to the newly built line */
  1728.   copy_string (textp, program->end_ptr);
  1729.  
  1730. /* Free old line and install new one */
  1731.   newtext = alloc (length_of (text_buffer) + 1);
  1732.   if (newtext == NIL_PTR) {
  1733.     ring_bell ();
  1734.     error ("Substitution failed: cannot allocate more memory", NIL_PTR);
  1735.     return NIL_PTR;
  1736.   }
  1737.   else {
  1738.     free_space (line->text);
  1739.     line->text = newtext;
  1740.     copy_string (line->text, text_buffer);
  1741.     return (line->text + (int) (textp - text_buffer));
  1742.   }
  1743. }
  1744.  
  1745. /*
  1746.  * GR () a replaces all matches from the current position until the end
  1747.  * of the file.
  1748.  */
  1749. void
  1750. GR ()
  1751. {
  1752.   change ("Global replace:", VALID, FALSE);
  1753. }
  1754.  
  1755. /*
  1756.  * Replace is substitute with confirmation dialogue.
  1757.  */
  1758. void
  1759. REPL ()
  1760. {
  1761.   change ("Global replace (with confirm):", VALID, TRUE);
  1762. }
  1763.  
  1764. /*
  1765.  * LR () replaces all matches on the current line.
  1766.  */
  1767. void
  1768. LR ()
  1769. {
  1770.   change ("Line replace:", NOT_VALID, FALSE);
  1771. }
  1772.  
  1773. /*
  1774.  * Search () calls get_expression to fetch the expression. If this went well,
  1775.  * the function match () is called which returns the line with the next match.
  1776.  * If this line is the NIL_LINE, it means that a match could not be found.
  1777.  * Find_x () and find_y () display the right page on the screen, and return
  1778.  * the right coordinates for x and y.
  1779.  * These coordinates are passed to move_to ().
  1780.  * Re_search () searches using the last search program and direction.
  1781.    Call graph of search procedures:
  1782. RS ------------\
  1783. SFW -\        > re_search > match -----------\
  1784. SRV --> search <                \
  1785.             \                 \
  1786.              > get_expression > compile      > line_check > check_string
  1787. GR  \            /                 /
  1788. REPL > change  <--------------------------------/
  1789. LR  /            \
  1790.          \ substitute
  1791.  
  1792.  */
  1793. REGEX * lastprogram = NIL_REG;
  1794. FLAG lastmethod = NOT_VALID;    /* FORWARD, REVERSE */
  1795.  
  1796. char prevexpr [maxLINE_LEN];    /* Buffer for previous expr. */
  1797. FLAG prevmethod = NOT_VALID;    /* FORWARD, REVERSE */
  1798.  
  1799. void do_search ();
  1800.  
  1801. void
  1802. re_search ()
  1803. {
  1804.   do_search (lastprogram, lastmethod);
  1805. }
  1806.  
  1807. void
  1808. prev_search ()
  1809. {
  1810.   search_for (prevexpr, prevmethod);
  1811. }
  1812.  
  1813. void
  1814. search (message, method)
  1815.   char * message;
  1816.   FLAG method;
  1817. {
  1818.   register REGEX * program;
  1819.   char prevexp_buf [maxLINE_LEN];    /* Buffer for previous expr. */
  1820.  
  1821.   if (lastmethod != NOT_VALID) copy_string (prevexp_buf, exp_buf);
  1822.  
  1823. /* Get the expression */
  1824.   if ((program = get_expression (message)) == NIL_REG)
  1825.     return;
  1826.  
  1827.   if (program != NIL_REG && lastmethod != NOT_VALID) {
  1828.     copy_string (prevexpr, prevexp_buf);
  1829.     prevmethod = lastmethod;
  1830.   }
  1831.   lastprogram = program;
  1832.   lastmethod = method;
  1833.  
  1834.   re_search ();
  1835. }
  1836.  
  1837. void
  1838. search_for (expr, method)
  1839.   char * expr;
  1840.   FLAG method;
  1841. {
  1842.   register REGEX * program;
  1843.  
  1844. /* make the expression */
  1845.   if ((program = make_expression (expr)) == NIL_REG)
  1846.     return;
  1847.   do_search (program, method);
  1848. }
  1849.  
  1850. void
  1851. do_search (program, method)
  1852.   register REGEX * program;
  1853.   FLAG method;
  1854. {
  1855.   register LINE * match_line;
  1856.  
  1857.   if (method == NOT_VALID) {
  1858.     error ("No previous search", NIL_PTR);
  1859.     return;
  1860.   }
  1861.   if (program == NIL_REG) {
  1862.     error ("No previous search expression", NIL_PTR);
  1863.     return;
  1864.   }
  1865.  
  1866.   set_cursor (0, YMAX);
  1867.   clear_status ();
  1868.   flush ();
  1869. /* Find the match */
  1870.   if ((match_line = match (program, cur_text, method)) == NIL_LINE) {
  1871.     if (quit == TRUE) {
  1872.         status_msg ("Aborted");
  1873. /*        swallow_dummy_quit_char (); */
  1874.         quit = FALSE;
  1875.     }
  1876.     else
  1877.         status_msg ("Pattern not found");
  1878.     return;
  1879.   }
  1880.  
  1881. /*  clear_status (); */
  1882.   move_address (program->start_ptr, find_y (match_line));
  1883. }
  1884.  
  1885. /* Now the search and replace utilities */
  1886. /* ------------------------------------ */
  1887. /* Opcodes for characters */
  1888. #define    NORMAL        0x0200
  1889. #define DOT        0x0400
  1890. #define EOLN        0x0800
  1891. #define STAR        0x1000
  1892. #define BRACKET        0x2000
  1893. #define NEGATE        0x0100
  1894. #define DONE        0x4000
  1895.  
  1896. /* Mask for opcodes and characters */
  1897. #define LOW_BYTE    0x00FF
  1898. #define HIGH_BYTE    0xFF00
  1899.  
  1900. /* Previous is the contents of the previous address (ptr) points to */
  1901. #define previous(ptr)        (* ((ptr) - 1))
  1902.  
  1903. /* Buffer to store outcome of compilation */
  1904. int exp_buffer [BLOCK_SIZE];
  1905.  
  1906. /* Errors often used */
  1907. char * too_long = "Regular expression too long";
  1908.  
  1909. /*
  1910.  * Reg_error () is called by compile () if something went wrong. It sets the
  1911.  * status of the structure to error, and assigns the error field of the union.
  1912.  */
  1913. #define reg_error(str)    program->status = REG_ERROR, \
  1914.                     program->result.err_mess = (str)
  1915.  
  1916. /*
  1917.  * Bcopy copies `bytes' bytes from the `from' address into the `to' address.
  1918.  */
  1919. #ifdef vms
  1920. #define defbcopy
  1921. #endif
  1922. #ifdef msdos
  1923. #define defbcopy
  1924. #endif
  1925.  
  1926. #ifdef defbcopy    /* otherwise also in standard library */
  1927. void
  1928. bcopy (from, to, bytes)
  1929.   register char * from, * to;
  1930.   register int bytes;
  1931. {
  1932.   while (bytes --)
  1933.     * to ++ = * from ++;
  1934. }
  1935. #else
  1936. #ifdef sysV
  1937. #define bcopy(from, to, len)    memcpy (to, from, len)
  1938. #else
  1939. extern void bcopy ();
  1940. #endif
  1941. #endif
  1942.  
  1943. /*
  1944.  * Finished () is called when everything went right during compilation. It
  1945.  * allocates space for the expression, and copies the expression buffer into
  1946.  * this field.
  1947.  */
  1948. FLAG
  1949. finished (program, last_exp)
  1950.   register REGEX * program;
  1951.   int * last_exp;
  1952. {
  1953.   register int length = (int) (last_exp - exp_buffer) * sizeof (int);
  1954. /* Allocate space */
  1955.   program->result.expression = (int *) alloc (length);
  1956.   if (program->result.expression == NIL_INT) {
  1957.     ring_bell ();
  1958.     error ("Cannot allocate memory for search expression", NIL_PTR);
  1959.     return ERRORS;
  1960.   }
  1961.   else {    /* Copy expression. (expression consists of ints!) */
  1962.     bcopy (exp_buffer, program->result.expression, length);
  1963.     return FINE;
  1964.   }
  1965. }
  1966.  
  1967. /*
  1968.  * Compile compiles the pattern into a more comprehensible form and returns a
  1969.  * REGEX structure. If something went wrong, the status field of the structure
  1970.  * is set to REG_ERROR and an error message is set into the err_mess field of
  1971.  * the union. If all went well the expression is saved and the expression
  1972.  * pointer is set to the saved (and compiled) expression.
  1973.  */
  1974. FLAG
  1975. compile (pattern, program)
  1976.   register uchar * pattern;    /* Pointer to pattern */
  1977.   REGEX * program;
  1978. {
  1979.   register int * expression = exp_buffer;
  1980.   int * prev_char;        /* Pointer to previous compiled atom */
  1981.   int * acct_field = NIL_INT;    /* Pointer to last BRACKET start */
  1982.   FLAG negate;            /* Negate flag for BRACKET */
  1983.   uchar low_char;        /* Index for chars in BRACKET */
  1984.   uchar c;
  1985.  
  1986. /* Check for begin of line */
  1987.   if (* pattern == '^') {
  1988.     program->status = BEGIN_LINE;
  1989.     pattern ++;
  1990.   }
  1991.   else {
  1992.     program->status = 0;
  1993. /* If the first character is a '*' we have to assign it here. */
  1994.     if (* pattern == '*') {
  1995.         * expression ++ = '*' + NORMAL;
  1996.         pattern ++;
  1997.     }
  1998.   }
  1999.  
  2000.   for (; ;) {    c = * pattern ++;
  2001.     switch (c) {
  2002.     case '.' :
  2003.         * expression ++ = DOT;
  2004.         break;
  2005.     case '$' :
  2006.         /*
  2007.          * Only means EOLN if it is the last char of the pattern
  2008.          */
  2009.         if (* pattern == '\0') {
  2010.             * expression ++ = EOLN | DONE;
  2011.             program->status |= END_LINE;
  2012.             return finished (program, expression);
  2013.         }
  2014.         else
  2015.             * expression ++ = NORMAL + '$';
  2016.         break;
  2017.     case '\0' :
  2018.         * expression ++ = DONE;
  2019.         return finished (program, expression);
  2020.     case '\\' :
  2021.         /* If last char, it must! mean a normal '\' */
  2022.         if (* pattern == '\0')
  2023.             * expression ++ = NORMAL + '\\';
  2024.         else
  2025.             * expression ++ = NORMAL + * pattern ++;
  2026.         break;
  2027.     case '*' :
  2028.         /*
  2029.          * If the previous expression was a [] find out the
  2030.          * begin of the list, and adjust the opcode.
  2031.          */
  2032.         prev_char = expression - 1;
  2033.         if (* prev_char & BRACKET)
  2034.             * (expression - (* acct_field & LOW_BYTE)) |= STAR;
  2035.         else
  2036.             * prev_char |= STAR;
  2037.         break;
  2038.     case '[' :
  2039.         /*
  2040.          * First field in expression gives information about
  2041.          * the list.
  2042.          * The opcode consists of BRACKET and if necessary
  2043.          * NEGATE to indicate that the list should be negated
  2044.          * and/or STAR to indicate a number of sequence of this
  2045.          * list.
  2046.          * The lower byte contains the length of the list.
  2047.          */
  2048.         acct_field = expression ++;
  2049.         if (* pattern == '^') {    /* List must be negated */
  2050.             pattern ++;
  2051.             negate = TRUE;
  2052.         }
  2053.         else
  2054.             negate = FALSE;
  2055.         while (* pattern != ']') {
  2056.             if (* pattern == '\0') {
  2057.                 reg_error ("Missing ]");
  2058.                 return FINE;
  2059.             }
  2060.             if (* pattern == '\\')
  2061.                 pattern ++;
  2062.             * expression ++ = * pattern ++;
  2063.             if (* pattern == '-') {
  2064.                         /* Make list of chars */
  2065.                 low_char = previous (pattern);
  2066.                 pattern ++;    /* Skip '-' */
  2067.                 if (low_char ++ > * pattern) {
  2068.                     reg_error ("Bad range in [a-z]");
  2069.                     return FINE;
  2070.                 }
  2071.                 /* Build list */
  2072.                 while (low_char <= * pattern
  2073.                     && low_char != '\0')
  2074.                  /* avoid wrap-around beyond '\377' (loop!) */
  2075.                     * expression ++ = low_char ++;
  2076.                 pattern ++;
  2077.             }
  2078.             if (expression >= & exp_buffer [BLOCK_SIZE]) {
  2079.                 reg_error (too_long);
  2080.                 return FINE;
  2081.             }
  2082.         }
  2083.         pattern ++;            /* Skip ']' */
  2084.         /* Assign length of list in acct field */
  2085.         if ((* acct_field = (int) (expression - acct_field)) == 1) {
  2086.             reg_error ("Empty []");
  2087.             return FINE;
  2088.         }
  2089.         /* Assign negate and bracket field */
  2090.         * acct_field |= BRACKET;
  2091.         if (negate == TRUE)
  2092.             * acct_field |= NEGATE;
  2093.         /*
  2094.          * Add BRACKET to opcode of last char in field because
  2095.          * a '*' may be following the list.
  2096.          */
  2097.         previous (expression) |= BRACKET;
  2098.         break;
  2099.     default :
  2100.         * expression ++ = c + NORMAL;
  2101.     }
  2102.     if (expression == & exp_buffer [BLOCK_SIZE]) {
  2103.         reg_error (too_long);
  2104.         return FINE;
  2105.     }
  2106.   }
  2107.   /* NOTREACHED */
  2108. }
  2109.  
  2110. /*
  2111.  * Match gets as argument the program, pointer to place in current line to
  2112.  * start from and the method to search for (either FORWARD or REVERSE).
  2113.  * Match () will look through the whole file until a match is found.
  2114.  * NIL_LINE is returned if no match could be found.
  2115.  */
  2116. LINE *
  2117. match (program, string, method)
  2118.   REGEX * program;
  2119.   uchar * string;
  2120.   register FLAG method;
  2121. {
  2122.   register LINE * line = cur_line;
  2123.   uchar old_char;                /* For saving chars */
  2124.  
  2125. /* Corrupted program */
  2126.   if (program->status == REG_ERROR)
  2127.     return NIL_LINE;
  2128.  
  2129. /* Check part of text first */
  2130.   if (! (program->status & BEGIN_LINE)) {
  2131.     if (method == FORWARD) {
  2132.         if (line_check (program, string + 1, method) == MATCH)
  2133.             return cur_line;    /* Match found */
  2134.     }
  2135.     else if (! (program->status & END_LINE)) {
  2136.         old_char = * string;    /* Save char and */
  2137.         * string = '\n';    /* Assign '\n' for line_check */
  2138.         if (line_check (program, line->text, method) == MATCH) {
  2139.             * string = old_char; /* Restore char */
  2140.             return cur_line;    /* Found match */
  2141.         }
  2142.         * string = old_char;    /* No match, but restore char */
  2143.     }
  2144.   }
  2145.  
  2146. /* No match in last (or first) part of line. Check out rest of file */
  2147.   do {
  2148.     line = (method == FORWARD) ? line->next : line->prev;
  2149.     if (line->text == NIL_PTR) {    /* Header/tail */
  2150.         status_msg ("Search wrapped around end of file");
  2151.         continue;
  2152.         }
  2153.     if (line_check (program, line->text, method) == MATCH)
  2154.         return line;
  2155.   } while (line != cur_line && quit == FALSE);
  2156.  
  2157. /* No match found. */
  2158.   return NIL_LINE;
  2159. }
  2160.  
  2161. /*
  2162.  * Line_check () checks the line (or rather string) for a match. Method
  2163.  * indicates FORWARD or REVERSE search. It scans through the whole string
  2164.  * until a match is found, or the end of the string is reached.
  2165.  */
  2166. int
  2167. line_check (program, string, method)
  2168.   register REGEX * program;
  2169.   char * string;
  2170.   FLAG method;
  2171. {
  2172.   register char * textp = string;
  2173.  
  2174. /* Assign start_ptr field. We might find a match right away! */
  2175.   program->start_ptr = textp;
  2176.  
  2177. /* If the match must be anchored, just check the string. */
  2178.   if (program->status & BEGIN_LINE)
  2179.     return check_string (program, string, NIL_INT);
  2180.  
  2181.   if (method == REVERSE) {
  2182.     /* First move to the end of the string */
  2183.     for (textp = string; * textp != '\n'; textp ++)
  2184.         ;
  2185.     /* Start checking string until the begin of the string is met */
  2186.     while (textp >= string) {
  2187.         program->start_ptr = textp;
  2188.         if (check_string (program, textp --, NIL_INT))
  2189.             return MATCH;
  2190.     }
  2191.   }
  2192.   else {
  2193.     /* Move through the string until the end of it is found */
  2194.     while (quit == FALSE && * textp != '\0') {
  2195.         program->start_ptr = textp;
  2196.         if (check_string (program, textp, NIL_INT))
  2197.             return MATCH;
  2198.         if (* textp == '\n')
  2199.             break;
  2200.         textp ++;
  2201.     }
  2202.   }
  2203.  
  2204.   return NO_MATCH;
  2205. }
  2206.  
  2207. /*
  2208.  * Check_string () checks if a match can be found in the given string.
  2209.  * Whenever a STAR is found during matching, then the begin position of
  2210.  * the string is marked and the maximum number of matches is performed.
  2211.  * Then the function star () is called which starts to finish the match
  2212.  * from this position of the string (and expression).
  2213.  * Check () returns MATCH for a match, NO_MATCH if the string
  2214.  * couldn't be matched or REG_ERROR for an illegal opcode in expression.
  2215.  */
  2216. int
  2217. check_string (program, string, expression)
  2218.   REGEX * program;
  2219.   register uchar * string;
  2220.   int * expression;
  2221. {
  2222.   register int opcode;        /* Holds opcode of next expr. atom */
  2223.   uchar c;            /* Char that must be matched */
  2224.   uchar * mark;        /* For marking position */
  2225.   int star_fl;            /* A star has been born */
  2226.  
  2227.   if (expression == NIL_INT)
  2228.     expression = program->result.expression;
  2229.  
  2230. /* Loop until end of string or end of expression */
  2231.   while (quit == FALSE && ! (* expression & DONE) &&
  2232.                    * string != '\0' && * string != '\n') {
  2233.     c = * expression & LOW_BYTE;      /* Extract match char */
  2234.     opcode = * expression & HIGH_BYTE; /* Extract opcode */
  2235.     if ((star_fl = (opcode & STAR)) != 0) {  /* Check star occurrence */
  2236.         opcode &= ~STAR;      /* Strip opcode */
  2237.         mark = string;          /* Mark current position */
  2238.     }
  2239.     expression ++;        /* Increment expr. */
  2240.     switch (opcode) {
  2241.     case NORMAL :
  2242.         if (star_fl)
  2243.             while (* string ++ == c)    /* Skip all matches */
  2244.                 ;
  2245.         else if (* string ++ != c)
  2246.             return NO_MATCH;
  2247.         break;
  2248.     case DOT :
  2249.         string ++;
  2250.         if (star_fl)            /* Skip to eoln */
  2251.             while (* string != '\0' && * string ++ != '\n')
  2252.                 ;
  2253.         break;
  2254.     case NEGATE | BRACKET :
  2255.     case BRACKET :
  2256.          if (star_fl)
  2257.         while (in_list (expression, * string ++, c, opcode) == MATCH)
  2258.             ;
  2259.          else
  2260.         if (in_list (expression, * string ++, c, opcode) == NO_MATCH)
  2261.             return NO_MATCH;
  2262.         expression += c - 1;    /* Add length of list */
  2263.         break;
  2264.     default :
  2265.         /* panic ("Corrupted search program", NIL_PTR); */
  2266.         ring_bell ();
  2267.         error ("Corrupted search program", NIL_PTR);
  2268.         sleep (2);
  2269.         return NO_MATCH;
  2270.     }
  2271.     if (star_fl)
  2272.         return star (program, mark, string, expression);
  2273.   }
  2274.   if (* expression & DONE) {
  2275.     program->end_ptr = (char *) string;    /* Match ends here */
  2276.     /*
  2277.      * We might have found a match. The last thing to do is check
  2278.      * whether a '$' was given at the end of the expression, or
  2279.      * the match was found on a null string. (E.g. [a-z]* always
  2280.      * matches) unless a ^ or $ was included in the pattern.
  2281.      */
  2282.     if ((* expression & EOLN) && * string != '\n' && * string != '\0')
  2283.         return NO_MATCH;
  2284.     if (string == (uchar *) program->start_ptr
  2285.                     && ! (program->status & BEGIN_LINE)
  2286.                     && ! (* expression & EOLN))
  2287.         return NO_MATCH;
  2288.     return MATCH;
  2289.   }
  2290.   return NO_MATCH;
  2291. }
  2292.  
  2293. /*
  2294.  * Star () calls check_string () to find out the longest match possible.
  2295.  * It searches backwards until the (in check_string ()) marked position
  2296.  * is reached, or a match is found.
  2297.  */
  2298. int
  2299. star (program, end_position, string, expression)
  2300.   REGEX * program;
  2301.   register char * end_position;
  2302.   register char * string;
  2303.   int * expression;
  2304. {
  2305.   do {
  2306.     string --;
  2307.     if (check_string (program, string, expression))
  2308.         return MATCH;
  2309.   } while (string != end_position);
  2310.  
  2311.   return NO_MATCH;
  2312. }
  2313.  
  2314. /*
  2315.  * In_list () checks if the given character is in the list of []. If it is
  2316.  * it returns MATCH. If it isn't it returns NO_MATCH. These returns values
  2317.  * are reversed when the NEGATE field in the opcode is present.
  2318.  */
  2319. int
  2320. in_list (list, c, list_length, opcode)
  2321.   register int * list;
  2322.   uchar c;
  2323.   register int list_length;
  2324.   int opcode;
  2325. {
  2326.   if (c == '\0' || c == '\n')    /* End of string, never matches */
  2327.     return NO_MATCH;
  2328.   while (list_length -- > 1) {    /* > 1, don't check acct_field */
  2329.     if ((* list & LOW_BYTE) == c)
  2330.         return (opcode & NEGATE) ? NO_MATCH : MATCH;
  2331.     list ++;
  2332.   }
  2333.   return (opcode & NEGATE) ? MATCH : NO_MATCH;
  2334. }
  2335.  
  2336. /*  ==================================================================    *
  2337.  *                End                    *
  2338.  *  ==================================================================    */
  2339.